﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using GeoFramework;
using System.Drawing;

namespace MapControl
{
    public abstract class MapElement : IDisposable
    {
        public event EventHandler MapChanged;
        public event MapEventHandler Click;
        public event MapEventHandler DoubleClick;

        private Map map;
        private Map previousMap;
        private MapObserver clickObserver;
        private MapObserver doubleClickObserver;
        private Rectangle visibleAreaOnMap = new Rectangle(0, 0, 0, 0);
        private Rectangle clickableAreaOnMap = new Rectangle(0, 0, 0, 0);
        private Position position;

        public Map Map
        {
            get
            {
                return map;
            }
            set
            {
                if (map == value)
                    return;
                previousMap = map;
                map = value;
                if (MapChanged != null)
                    MapChanged(this, EventArgs.Empty);
            }
        }

        public string Text { get; set; }
        public Position Position 
        {
            get 
            {
                return position;
            }
            set
            {
                position = value;
                if(map != null)
                    map.Refresh();
            }
        }
        public bool IsInVisibleArea { get; private set; }
        public bool IsVisible { get; private set; }
        public Size Size { get; set; }
        protected Rectangle VisibleAreaOnMap
        {
            get
            {
                return visibleAreaOnMap;
            }
            private set
            {
                visibleAreaOnMap = value;
            }
        }

        protected Rectangle ClickableAreaOnMap
        {
            get
            {
                return clickableAreaOnMap;
            }
            private set
            {
                clickObserver.Area = value;
                doubleClickObserver.Area = value;
            }
        }

        public MapElement()
        {
            MapChanged += new EventHandler(MapElement_MapChanged);
            clickObserver = new MapObserver(VisibleAreaOnMap, new MapEventHandler(ObserverClickAction));
            doubleClickObserver = new MapObserver(VisibleAreaOnMap, new MapEventHandler(ObserverDoubleClickAction));
            Click += new MapEventHandler(MapElement_Click);
        }

        void MapElement_Click(object sender, MapEventArgs me)
        {
            if (!string.IsNullOrEmpty(Text))
                Map.ShowInfo(Text);
        }

        void MapElement_MapChanged(object sender, EventArgs e)
        {
            if (previousMap != null)
            {
                map.PositionChanged -= new EventHandler(map_PositionChanged);
                map.Paint -= new System.Windows.Forms.PaintEventHandler(map_Paint);
                map.RemoveClickObserver(clickObserver);
                map.RemoveDoubleClickObserver(doubleClickObserver);
            }

            if (map != null)
            {
                map.PositionChanged += new EventHandler(map_PositionChanged);
                map.Paint += new System.Windows.Forms.PaintEventHandler(map_Paint);
                map.AddClickObserver(clickObserver);
                map.AddDoubleClickObserver(doubleClickObserver);
                map.Refresh();
            }
        }

        void map_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
        {
            Calculate();
            if (IsInVisibleArea && IsVisible)
                Paint(e.Graphics);
        }

        void map_PositionChanged(object sender, EventArgs e)
        {
            Calculate();
        }

        public virtual void Show()
        {
            IsVisible = true;
        }

        public virtual void Hide()
        {
            IsVisible = false;
        }

        protected virtual Rectangle CalculateClickableArea()
        {
            return VisibleAreaOnMap;
        }

        private void Calculate()
        {
            PointD p = Map.MapProviderInstance.TilesManager.ProjectionCalculator.FromPosition(Position, Map.ZoomLevel);
            IsInVisibleArea = (p.X >= Map.ProjectionArea.X && p.X <= Map.ProjectionArea.X + Map.ProjectionArea.Width && p.Y >= Map.ProjectionArea.Y && p.Y <= Map.ProjectionArea.Y + Map.ProjectionArea.Height);
            VisibleAreaOnMap = IsInVisibleArea ? new Rectangle(Convert.ToInt32(p.X) - Map.ProjectionArea.X, Convert.ToInt32(p.Y) - Map.ProjectionArea.Y, Size.Width, Size.Height) : new Rectangle(-10, -10, 1, 1);
            ClickableAreaOnMap = IsInVisibleArea ? CalculateClickableArea() : new Rectangle(-10, -10, 1, 1);
        }

        private void ObserverClickAction(object sender, MapEventArgs me)
        {
            if (Click != null)
                Click(this, me);
        }

        private void ObserverDoubleClickAction(object sender, MapEventArgs me)
        {
            if (DoubleClick != null)
                DoubleClick(this, me);
        }

        protected abstract void Paint(Graphics g);

        #region IDisposable Members

        public void Dispose()
        {
            DisposeElement();
        }

        #endregion

        protected abstract void DisposeElement();
    }
}
